package cn.bugstack.mybatis.builder.xml;

import cn.bugstack.mybatis.builder.BaseBuilder;
import cn.bugstack.mybatis.builder.MapperBuilderAssistant;
import cn.bugstack.mybatis.builder.ResultMapResolver;
import cn.bugstack.mybatis.io.Resources;
import cn.bugstack.mybatis.mapping.ResultFlag;
import cn.bugstack.mybatis.mapping.ResultMapping;
import cn.bugstack.mybatis.session.Configuration;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.Reader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import static com.sun.beans.finder.ClassFinder.resolveClass;


/**
 * <mapper resource="mapper/User_Mapper.xml"/>
 *
 * <mapper namespace="org.mybatis.example.BlogMapper">
 *   <select id="selectBlog" parameterType="int" resultType="Blog">
 *    select * from Blog where id = #{id}
 *   </select>
 * </mapper>
 */
public class XmlMapperBuilder extends BaseBuilder {

    private Element element;

    private String resource;

    private MapperBuilderAssistant builderAssistant;



    public XmlMapperBuilder(Configuration configuration, Element element, String resource) {
        super(configuration);
        this.element = element;
        this.resource = resource;
        this.builderAssistant = new MapperBuilderAssistant(configuration, resource);
    }

    /**

     * @throws Exception
     */
    public void parse() throws Exception{
        String resource = element.attributeValue("resource");
        // <mapper resource="mapper/User_Mapper.xml"/>
        Reader resourceAsReader = Resources.getResourceAsReader(resource);
        Document document = new SAXReader().read(resourceAsReader);
        Element root = document.getRootElement();
        String namespace = root.attributeValue("namespace");
        if (namespace.equals("")) {
            throw new RuntimeException("Mapper's namespace cannot be empty");
        }
        builderAssistant.setCurrentNamespace(namespace);

        // 解析<resultMap>
        resultMapElements(root.elements("resultMap"));

        buildStatementFromContext(root.elements("select"),
                root.elements("insert"),
                root.elements("update"),
                root.elements("delete")
                );
    }

    private void resultMapElements(List<Element> resultMap) {
        for (Element element : resultMap) {
            resultMapElement(element, Collections.emptyList());
        }
    }

    /**
     *     <resultMap id="activityMap" type="cn.bugstack.mybatis.test.po.Activity">
     *         <id column="id" property="id"/>
     *         <result column="activity_id" property="activityId"/>
     *         <result column="activity_name" property="activityName"/>
     *         <result column="activity_desc" property="activityDesc"/>
     *         <result column="create_time" property="createTime"/>
     *         <result column="update_time" property="updateTime"/>
     *     </resultMap>
     * @param resultMapNode
     * @param additionalResultMappings
     */
    private void resultMapElement(Element resultMapNode, List<ResultMapping> additionalResultMappings) {
        String id = resultMapNode.attributeValue("id");
        String type = resultMapNode.attributeValue("type");
        Class<?> typeClass = resolveClass(type);

        List<ResultMapping> resultMappings = new ArrayList<>();
        resultMappings.addAll(additionalResultMappings);

        List<Element> resultMapChildren = resultMapNode.elements();

        for (Element resultMapChild : resultMapChildren) {
            List<ResultFlag> flags = new ArrayList<>();
            if ("id".equals(resultMapChild.getName())) {
                flags.add(ResultFlag.ID);
            }
            // 构建resultMapping
            resultMappings.add(buildResultMappingFromContext(resultMapChild, typeClass, flags));
        }

        // 使用MapperBuilderAssistant 将 构建 ResultMap 并存入 configuration
        ResultMapResolver resolver = new ResultMapResolver(builderAssistant, id, typeClass, resultMappings);

        resolver.resolve();

    }

    private ResultMapping buildResultMappingFromContext(Element resultMapChild, Class<?> resultTypeClass, List<ResultFlag> flags) {
        String property = resultMapChild.attributeValue("property");
        String column = resultMapChild.attributeValue("column");
        return builderAssistant.buildResultMapping(resultTypeClass, property, column, flags);
    }

    private void buildStatementFromContext(List<Element>... list) throws Exception {
        for (List<Element> elements : list) {
            for (Element element : elements) {
                /**
                 *   <select id="selectBlog" parameterType="int" resultType="Blog">
                 *     select * from Blog where id = #{id}
                 *   </select>
                 */
                XmlStatementBuilder xmlStatementBuilder = new XmlStatementBuilder(configuration, element, builderAssistant);
                xmlStatementBuilder.parseStatementNode();
            }
            // 需要将mapper接口注册到MapperRegistry
        }
        configuration.addMapper(Resources.classForName(builderAssistant.getCurrentNamespace()));
    }

}
